package model.code.line;

import event.TextReplacementEvent;
import model.code.CodeFileChunk;
import util.ASUtil;

import java.util.ArrayList;

/**
 * This is a {@link LineList} that is specifically only one {@link Line}.
 * @author Neil Dickson
 */
public class SingleLine extends Line implements LineList<SingleLine> {
	/**
	 * The {@link model.code.CodeFileChunk} containing this {@link model.code.line.LineList}
	 */
	protected CodeFileChunk	parent;
	protected int			locationType;
	protected int			locationIndex;

	protected ArrayList<LineObserver>	observers;

	public SingleLine(char[] text,CodeFileChunk parent, int locationType) {
		this(text,parent,locationType,0);
	}

	public SingleLine(char[] text,CodeFileChunk parent, int locationType, int locationIndex) {
		// NOTE: I can't specify this as its own list in the superclass constructor because Java won't let me pass this to it, so it's set just after.
		super(null,text);
		list = this;
		this.parent = parent;
		this.locationType = locationType;
		this.locationIndex = locationIndex;
		observers = null;
	}

	public void setLocation(CodeFileChunk parent, int locationType, int locationIndex) {
		this.parent = parent;
		this.locationType = locationType;
		this.locationIndex = locationIndex;
	}

	public CodeFileChunk getParent() {
		return parent;
	}

	public int getLocationType() {
		return locationType;
	}

	public int getLocationIndex() {
		return locationIndex;
	}

	public int size() {
		return 1;
	}

	public SingleLine get(int index) {
		if (index!=0) {
			throw new ArrayIndexOutOfBoundsException(index);
		}
		return this;
	}

	public int indexOf(SingleLine line) {
		return (line==this) ? 0 : -1;
	}

	public TextReplacementEvent insert(int line, int col, String text) {
		if (line!=0) {
			throw new ArrayIndexOutOfBoundsException(line);
		}
		return replace(col,col,text);
	}

	public TextReplacementEvent replaceAll(String text) {
		return replace(0,length,text);
	}
	public TextReplacementEvent replaceAll(LineObserver culprit,String text) {
		return replace(culprit,0,length,text);
	}

	public TextReplacementEvent replace(int startLine, int startCol, int endLine, int endCol, String text) {
		if (startLine!=0 || endLine!=0) {
			throw new ArrayIndexOutOfBoundsException((startLine!=0) ? startLine : endLine);
		}
		return replace(startCol,endCol,text);
	}

	public String getText(int startLine, int startCol, int endLine, int endCol) {
		if (startLine!=0 || endLine!=0) {
			throw new ArrayIndexOutOfBoundsException((startLine!=0) ? startLine : endLine);
		}
		return toString(startCol,endCol);
	}

	public SingleLine copy() {
		return copy(parent,locationIndex>>>16);
	}

	public SingleLine copy(CodeFileChunk parent) {
		return copy(parent,locationIndex>>>16);
	}

	public SingleLine copy(CodeFileChunk parent,int row) {
		return new SingleLine(ASUtil.subCharArray(text,0,length),parent,locationType,(locationIndex&0xFFFF)+(row<<16));
	}

	public TextReplacementEvent replace(int startCol, int endCol, String newText) {
		return replace(null,startCol,endCol,newText.toCharArray());
	}
	public TextReplacementEvent replace(LineObserver culprit,int startCol, int endCol, String newText) {
		return replace(culprit,startCol,endCol,newText.toCharArray());
	}
	public TextReplacementEvent replace(int startCol, int endCol, char[] newText) {
		return replace(null,startCol,endCol,newText);
	}
	public TextReplacementEvent replace(LineObserver culprit,int startCol, int endCol, char[] newText) {
		String previousText = toString();
		startCol = Math.max(0,Math.min(length,startCol));
		endCol = Math.max(0,Math.min(length,endCol));
		TextReplacementEvent textEvent = new TextReplacementEvent(list,new String(text,startCol,endCol-startCol),0,startCol,0,startCol+newText.length);
		if (endCol<startCol) {
			throw new ArrayIndexOutOfBoundsException(endCol-startCol);
		}
		replaceJustText(startCol,endCol,newText);
		updateAllBut(culprit,previousText);
		return textEvent;
	}

	public Line copy(LineList list, int index) {
		throw new ArrayIndexOutOfBoundsException("A SingleLine must be its own LineList, so copying it this way isn't valid.");
	}

	public Line copy(LineList list) {
		throw new ArrayIndexOutOfBoundsException("A SingleLine must be its own LineList, so copying it this way isn't valid.");
	}

	public synchronized void addObserver(LineObserver observer) {
		if (observer==null) {
			throw new NullPointerException();
		}
		if (observers==null) {
			observers = new ArrayList<LineObserver>();
		}
		if (!observers.contains(observer)) {
			observers.add(observer);
		}
	}
	public synchronized void removeObserver(LineObserver observer) {
		if (observers!=null && observers.contains(observer)) {
			observers.remove(observer);
		}
	}
	protected void updateAllBut(LineObserver culprit,String oldValue) {
		if (observers!=null) {
			for (LineObserver observer : observers) {
				if (observer!=culprit) {
					observer.valueChanged(this,oldValue);
				}
			}
		}
	}

	public ArrayList<LineObserver> getObservers() {
		return observers;
	}

	/**
	 * Things that observe {@link SingleLine}s need to implement this.
	 */
	public interface LineObserver {
		void valueChanged(SingleLine line, String oldValue);
	}
}